home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / DB.php < prev    next >
PHP Script  |  2004-10-01  |  34KB  |  1,115 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license,      |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Stig Bakken <ssb@php.net>                                   |
  17. // |          Tomas V.V.Cox <cox@idecnet.com>                             |
  18. // | Maintainer: Daniel Convissor <danielc@php.net>                       |
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: DB.php,v 1.59 2004/07/08 21:15:11 danielc Exp $
  22. //
  23. // Database independent query interface.
  24.  
  25.  
  26. require_once 'PEAR.php';
  27.  
  28. // {{{ constants
  29. // {{{ error codes
  30.  
  31. /*
  32.  * The method mapErrorCode in each DB_dbtype implementation maps
  33.  * native error codes to one of these.
  34.  *
  35.  * If you add an error code here, make sure you also add a textual
  36.  * version of it in DB::errorMessage().
  37.  */
  38. define('DB_OK',                         1);
  39. define('DB_ERROR',                     -1);
  40. define('DB_ERROR_SYNTAX',              -2);
  41. define('DB_ERROR_CONSTRAINT',          -3);
  42. define('DB_ERROR_NOT_FOUND',           -4);
  43. define('DB_ERROR_ALREADY_EXISTS',      -5);
  44. define('DB_ERROR_UNSUPPORTED',         -6);
  45. define('DB_ERROR_MISMATCH',            -7);
  46. define('DB_ERROR_INVALID',             -8);
  47. define('DB_ERROR_NOT_CAPABLE',         -9);
  48. define('DB_ERROR_TRUNCATED',          -10);
  49. define('DB_ERROR_INVALID_NUMBER',     -11);
  50. define('DB_ERROR_INVALID_DATE',       -12);
  51. define('DB_ERROR_DIVZERO',            -13);
  52. define('DB_ERROR_NODBSELECTED',       -14);
  53. define('DB_ERROR_CANNOT_CREATE',      -15);
  54. define('DB_ERROR_CANNOT_DELETE',      -16);
  55. define('DB_ERROR_CANNOT_DROP',        -17);
  56. define('DB_ERROR_NOSUCHTABLE',        -18);
  57. define('DB_ERROR_NOSUCHFIELD',        -19);
  58. define('DB_ERROR_NEED_MORE_DATA',     -20);
  59. define('DB_ERROR_NOT_LOCKED',         -21);
  60. define('DB_ERROR_VALUE_COUNT_ON_ROW', -22);
  61. define('DB_ERROR_INVALID_DSN',        -23);
  62. define('DB_ERROR_CONNECT_FAILED',     -24);
  63. define('DB_ERROR_EXTENSION_NOT_FOUND',-25);
  64. define('DB_ERROR_ACCESS_VIOLATION',   -26);
  65. define('DB_ERROR_NOSUCHDB',           -27);
  66. define('DB_ERROR_CONSTRAINT_NOT_NULL',-29);
  67.  
  68.  
  69. // }}}
  70. // {{{ prepared statement-related
  71.  
  72.  
  73. /*
  74.  * These constants are used when storing information about prepared
  75.  * statements (using the "prepare" method in DB_dbtype).
  76.  *
  77.  * The prepare/execute model in DB is mostly borrowed from the ODBC
  78.  * extension, in a query the "?" character means a scalar parameter.
  79.  * There are two extensions though, a "&" character means an opaque
  80.  * parameter.  An opaque parameter is simply a file name, the real
  81.  * data are in that file (useful for putting uploaded files into your
  82.  * database and such). The "!" char means a parameter that must be
  83.  * left as it is.
  84.  * They modify the quote behavoir:
  85.  * DB_PARAM_SCALAR (?) => 'original string quoted'
  86.  * DB_PARAM_OPAQUE (&) => 'string from file quoted'
  87.  * DB_PARAM_MISC   (!) => original string
  88.  */
  89. define('DB_PARAM_SCALAR', 1);
  90. define('DB_PARAM_OPAQUE', 2);
  91. define('DB_PARAM_MISC',   3);
  92.  
  93.  
  94. // }}}
  95. // {{{ binary data-related
  96.  
  97.  
  98. /*
  99.  * These constants define different ways of returning binary data
  100.  * from queries.  Again, this model has been borrowed from the ODBC
  101.  * extension.
  102.  *
  103.  * DB_BINMODE_PASSTHRU sends the data directly through to the browser
  104.  * when data is fetched from the database.
  105.  * DB_BINMODE_RETURN lets you return data as usual.
  106.  * DB_BINMODE_CONVERT returns data as well, only it is converted to
  107.  * hex format, for example the string "123" would become "313233".
  108.  */
  109. define('DB_BINMODE_PASSTHRU', 1);
  110. define('DB_BINMODE_RETURN',   2);
  111. define('DB_BINMODE_CONVERT',  3);
  112.  
  113.  
  114. // }}}
  115. // {{{ fetch modes
  116.  
  117.  
  118. /**
  119.  * This is a special constant that tells DB the user hasn't specified
  120.  * any particular get mode, so the default should be used.
  121.  */
  122. define('DB_FETCHMODE_DEFAULT', 0);
  123.  
  124. /**
  125.  * Column data indexed by numbers, ordered from 0 and up
  126.  */
  127. define('DB_FETCHMODE_ORDERED', 1);
  128.  
  129. /**
  130.  * Column data indexed by column names
  131.  */
  132. define('DB_FETCHMODE_ASSOC', 2);
  133.  
  134. /**
  135.  * Column data as object properties
  136.  */
  137. define('DB_FETCHMODE_OBJECT', 3);
  138.  
  139. /**
  140.  * For multi-dimensional results: normally the first level of arrays
  141.  * is the row number, and the second level indexed by column number or name.
  142.  * DB_FETCHMODE_FLIPPED switches this order, so the first level of arrays
  143.  * is the column name, and the second level the row number.
  144.  */
  145. define('DB_FETCHMODE_FLIPPED', 4);
  146.  
  147. /* for compatibility */
  148. define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED);
  149. define('DB_GETMODE_ASSOC',   DB_FETCHMODE_ASSOC);
  150. define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED);
  151.  
  152.  
  153. // }}}
  154. // {{{ tableInfo() && autoPrepare()-related
  155.  
  156.  
  157. /**
  158.  * these are constants for the tableInfo-function
  159.  * they are bitwised or'ed. so if there are more constants to be defined
  160.  * in the future, adjust DB_TABLEINFO_FULL accordingly
  161.  */
  162. define('DB_TABLEINFO_ORDER', 1);
  163. define('DB_TABLEINFO_ORDERTABLE', 2);
  164. define('DB_TABLEINFO_FULL', 3);
  165.  
  166. /*
  167.  * Used by autoPrepare()
  168.  */
  169. define('DB_AUTOQUERY_INSERT', 1);
  170. define('DB_AUTOQUERY_UPDATE', 2);
  171.  
  172.  
  173. // }}}
  174. // {{{ portability modes
  175.  
  176.  
  177. /**
  178.  * Portability: turn off all portability features.
  179.  * @see DB_common::setOption()
  180.  */
  181. define('DB_PORTABILITY_NONE', 0);
  182.  
  183. /**
  184.  * Portability: convert names of tables and fields to lower case
  185.  * when using the get*(), fetch*() and tableInfo() methods.
  186.  * @see DB_common::setOption()
  187.  */
  188. define('DB_PORTABILITY_LOWERCASE', 1);
  189.  
  190. /**
  191.  * Portability: right trim the data output by get*() and fetch*().
  192.  * @see DB_common::setOption()
  193.  */
  194. define('DB_PORTABILITY_RTRIM', 2);
  195.  
  196. /**
  197.  * Portability: force reporting the number of rows deleted.
  198.  * @see DB_common::setOption()
  199.  */
  200. define('DB_PORTABILITY_DELETE_COUNT', 4);
  201.  
  202. /**
  203.  * Portability: enable hack that makes numRows() work in Oracle.
  204.  * @see DB_common::setOption()
  205.  */
  206. define('DB_PORTABILITY_NUMROWS', 8);
  207.  
  208. /**
  209.  * Portability: makes certain error messages in certain drivers compatible
  210.  * with those from other DBMS's.
  211.  *
  212.  * + mysql, mysqli:  change unique/primary key constraints
  213.  *   DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT
  214.  *
  215.  * + odbc(access):  MS's ODBC driver reports 'no such field' as code
  216.  *   07001, which means 'too few parameters.'  When this option is on
  217.  *   that code gets mapped to DB_ERROR_NOSUCHFIELD.
  218.  *
  219.  * @see DB_common::setOption()
  220.  */
  221. define('DB_PORTABILITY_ERRORS', 16);
  222.  
  223. /**
  224.  * Portability: convert null values to empty strings in data output by
  225.  * get*() and fetch*().
  226.  * @see DB_common::setOption()
  227.  */
  228. define('DB_PORTABILITY_NULL_TO_EMPTY', 32);
  229.  
  230. /**
  231.  * Portability: turn on all portability features.
  232.  * @see DB_common::setOption()
  233.  */
  234. define('DB_PORTABILITY_ALL', 63);
  235.  
  236. // }}}
  237.  
  238.  
  239. // }}}
  240. // {{{ class DB
  241.  
  242. /**
  243.  * The main "DB" class is simply a container class with some static
  244.  * methods for creating DB objects as well as some utility functions
  245.  * common to all parts of DB.
  246.  *
  247.  * The object model of DB is as follows (indentation means inheritance):
  248.  *
  249.  * DB           The main DB class.  This is simply a utility class
  250.  *              with some "static" methods for creating DB objects as
  251.  *              well as common utility functions for other DB classes.
  252.  *
  253.  * DB_common    The base for each DB implementation.  Provides default
  254.  * |            implementations (in OO lingo virtual methods) for
  255.  * |            the actual DB implementations as well as a bunch of
  256.  * |            query utility functions.
  257.  * |
  258.  * +-DB_mysql   The DB implementation for MySQL.  Inherits DB_common.
  259.  *              When calling DB::factory or DB::connect for MySQL
  260.  *              connections, the object returned is an instance of this
  261.  *              class.
  262.  *
  263.  * @package  DB
  264.  * @author   Stig Bakken <ssb@php.net>
  265.  * @author   Tomas V.V.Cox <cox@idecnet.com>
  266.  * @since    PHP 4.0
  267.  * @version  $Id: DB.php,v 1.59 2004/07/08 21:15:11 danielc Exp $
  268.  * @category Database
  269.  */
  270. class DB
  271. {
  272.     // {{{ &factory()
  273.  
  274.     /**
  275.      * Create a new DB object for the specified database type.
  276.      *
  277.      * Allows creation of a DB_<driver> object from which the object's
  278.      * methods can be utilized without actually connecting to a database.
  279.      *
  280.      * @param string $type    database type, for example "mysql"
  281.      * @param array  $options associative array of option names and values
  282.      *
  283.      * @return object  a new DB object.  On error, an error object.
  284.      *
  285.      * @see DB_common::setOption()
  286.      * @access public
  287.      */
  288.     function &factory($type, $options = false)
  289.     {
  290.         if (!is_array($options)) {
  291.             $options = array('persistent' => $options);
  292.         }
  293.  
  294.         if (isset($options['debug']) && $options['debug'] >= 2) {
  295.             // expose php errors with sufficient debug level
  296.             include_once "DB/{$type}.php";
  297.         } else {
  298.             @include_once "DB/{$type}.php";
  299.         }
  300.  
  301.         $classname = "DB_${type}";
  302.  
  303.         if (!class_exists($classname)) {
  304.             $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null,
  305.                                     "Unable to include the DB/{$type}.php file",
  306.                                     'DB_Error', true);
  307.             return $tmp;
  308.         }
  309.  
  310.         @$obj =& new $classname;
  311.  
  312.         foreach ($options as $option => $value) {
  313.             $test = $obj->setOption($option, $value);
  314.             if (DB::isError($test)) {
  315.                 return $test;
  316.             }
  317.         }
  318.  
  319.         return $obj;
  320.     }
  321.  
  322.     // }}}
  323.     // {{{ &connect()
  324.  
  325.     /**
  326.      * Create a new DB object and connect to the specified database.
  327.      *
  328.      * Example 1.
  329.      * <code> <?php
  330.      * require_once 'DB.php';
  331.      *
  332.      * $dsn = 'mysql://user:password@host/database'
  333.      * $options = array(
  334.      *     'debug'       => 2,
  335.      *     'portability' => DB_PORTABILITY_ALL,
  336.      * );
  337.      *
  338.      * $dbh =& DB::connect($dsn, $options);
  339.      * if (DB::isError($dbh)) {
  340.      *     die($dbh->getMessage());
  341.      * }
  342.      * ?></code>
  343.      *
  344.      * @param mixed $dsn      string "data source name" or an array in the
  345.      *                        format returned by DB::parseDSN()
  346.      *
  347.      * @param array $options  an associative array of option names and
  348.      *                        their values
  349.      *
  350.      * @return object  a newly created DB connection object, or a DB
  351.      *                 error object on error
  352.      *
  353.      * @see DB::parseDSN(), DB_common::setOption(), DB::isError()
  354.      * @access public
  355.      */
  356.     function &connect($dsn, $options = array())
  357.     {
  358.         $dsninfo = DB::parseDSN($dsn);
  359.         $type = $dsninfo['phptype'];
  360.  
  361.         if (!is_array($options)) {
  362.             /*
  363.              * For backwards compatibility.  $options used to be boolean,
  364.              * indicating whether the connection should be persistent.
  365.              */
  366.             $options = array('persistent' => $options);
  367.         }
  368.  
  369.         if (isset($options['debug']) && $options['debug'] >= 2) {
  370.             // expose php errors with sufficient debug level
  371.             include_once "DB/${type}.php";
  372.         } else {
  373.             @include_once "DB/${type}.php";
  374.         }
  375.  
  376.         $classname = "DB_${type}";
  377.         if (!class_exists($classname)) {
  378.             $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null,
  379.                                     "Unable to include the DB/{$type}.php file for `$dsn'",
  380.                                     'DB_Error', true);
  381.             return $tmp;
  382.         }
  383.  
  384.         @$obj =& new $classname;
  385.  
  386.         foreach ($options as $option => $value) {
  387.             $test = $obj->setOption($option, $value);
  388.             if (DB::isError($test)) {
  389.                 return $test;
  390.             }
  391.         }
  392.  
  393.         $err = $obj->connect($dsninfo, $obj->getOption('persistent'));
  394.         if (DB::isError($err)) {
  395.             $err->addUserInfo($dsn);
  396.             return $err;
  397.         }
  398.  
  399.         return $obj;
  400.     }
  401.  
  402.     // }}}
  403.     // {{{ apiVersion()
  404.  
  405.     /**
  406.      * Return the DB API version
  407.      *
  408.      * @return int the DB API version number
  409.      *
  410.      * @access public
  411.      */
  412.     function apiVersion()
  413.     {
  414.         return 2;
  415.     }
  416.  
  417.     // }}}
  418.     // {{{ isError()
  419.  
  420.     /**
  421.      * Tell whether a result code from a DB method is an error
  422.      *
  423.      * @param int $value result code
  424.      *
  425.      * @return bool whether $value is an error
  426.      *
  427.      * @access public
  428.      */
  429.     function isError($value)
  430.     {
  431.         return is_a($value, 'DB_Error');
  432.     }
  433.  
  434.     // }}}
  435.     // {{{ isConnection()
  436.  
  437.     /**
  438.      * Tell whether a value is a DB connection
  439.      *
  440.      * @param mixed $value value to test
  441.      *
  442.      * @return bool whether $value is a DB connection
  443.      *
  444.      * @access public
  445.      */
  446.     function isConnection($value)
  447.     {
  448.         return (is_object($value) &&
  449.                 is_subclass_of($value, 'db_common') &&
  450.                 method_exists($value, 'simpleQuery'));
  451.     }
  452.  
  453.     // }}}
  454.     // {{{ isManip()
  455.  
  456.     /**
  457.      * Tell whether a query is a data manipulation query (insert,
  458.      * update or delete) or a data definition query (create, drop,
  459.      * alter, grant, revoke).
  460.      *
  461.      * @access public
  462.      *
  463.      * @param string $query the query
  464.      *
  465.      * @return boolean whether $query is a data manipulation query
  466.      */
  467.     function isManip($query)
  468.     {
  469.         $manips = 'INSERT|UPDATE|DELETE|LOAD DATA|'.'REPLACE|CREATE|DROP|'.
  470.                   'ALTER|GRANT|REVOKE|'.'LOCK|UNLOCK';
  471.         if (preg_match('/^\s*"?('.$manips.')\s+/i', $query)) {
  472.             return true;
  473.         }
  474.         return false;
  475.     }
  476.  
  477.     // }}}
  478.     // {{{ errorMessage()
  479.  
  480.     /**
  481.      * Return a textual error message for a DB error code
  482.      *
  483.      * @param integer $value error code
  484.      *
  485.      * @return string error message, or false if the error code was
  486.      * not recognized
  487.      */
  488.     function errorMessage($value)
  489.     {
  490.         static $errorMessages;
  491.         if (!isset($errorMessages)) {
  492.             $errorMessages = array(
  493.                 DB_ERROR                    => 'unknown error',
  494.                 DB_ERROR_ALREADY_EXISTS     => 'already exists',
  495.                 DB_ERROR_CANNOT_CREATE      => 'can not create',
  496.                 DB_ERROR_CANNOT_DELETE      => 'can not delete',
  497.                 DB_ERROR_CANNOT_DROP        => 'can not drop',
  498.                 DB_ERROR_CONSTRAINT         => 'constraint violation',
  499.                 DB_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
  500.                 DB_ERROR_DIVZERO            => 'division by zero',
  501.                 DB_ERROR_INVALID            => 'invalid',
  502.                 DB_ERROR_INVALID_DATE       => 'invalid date or time',
  503.                 DB_ERROR_INVALID_NUMBER     => 'invalid number',
  504.                 DB_ERROR_MISMATCH           => 'mismatch',
  505.                 DB_ERROR_NODBSELECTED       => 'no database selected',
  506.                 DB_ERROR_NOSUCHFIELD        => 'no such field',
  507.                 DB_ERROR_NOSUCHTABLE        => 'no such table',
  508.                 DB_ERROR_NOT_CAPABLE        => 'DB backend not capable',
  509.                 DB_ERROR_NOT_FOUND          => 'not found',
  510.                 DB_ERROR_NOT_LOCKED         => 'not locked',
  511.                 DB_ERROR_SYNTAX             => 'syntax error',
  512.                 DB_ERROR_UNSUPPORTED        => 'not supported',
  513.                 DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row',
  514.                 DB_ERROR_INVALID_DSN        => 'invalid DSN',
  515.                 DB_ERROR_CONNECT_FAILED     => 'connect failed',
  516.                 DB_OK                       => 'no error',
  517.                 DB_ERROR_NEED_MORE_DATA     => 'insufficient data supplied',
  518.                 DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found',
  519.                 DB_ERROR_NOSUCHDB           => 'no such database',
  520.                 DB_ERROR_ACCESS_VIOLATION   => 'insufficient permissions',
  521.                 DB_ERROR_TRUNCATED          => 'truncated'
  522.             );
  523.         }
  524.  
  525.         if (DB::isError($value)) {
  526.             $value = $value->getCode();
  527.         }
  528.  
  529.         return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[DB_ERROR];
  530.     }
  531.  
  532.     // }}}
  533.     // {{{ parseDSN()
  534.  
  535.     /**
  536.      * Parse a data source name.
  537.      *
  538.      * Additional keys can be added by appending a URI query string to the
  539.      * end of the DSN.
  540.      *
  541.      * The format of the supplied DSN is in its fullest form:
  542.      * <code>
  543.      *  phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true
  544.      * </code>
  545.      *
  546.      * Most variations are allowed:
  547.      * <code>
  548.      *  phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644
  549.      *  phptype://username:password@hostspec/database_name
  550.      *  phptype://username:password@hostspec
  551.      *  phptype://username@hostspec
  552.      *  phptype://hostspec/database
  553.      *  phptype://hostspec
  554.      *  phptype(dbsyntax)
  555.      *  phptype
  556.      * </code>
  557.      *
  558.      * @param string $dsn Data Source Name to be parsed
  559.      *
  560.      * @return array an associative array with the following keys:
  561.      *  + phptype:  Database backend used in PHP (mysql, odbc etc.)
  562.      *  + dbsyntax: Database used with regards to SQL syntax etc.
  563.      *  + protocol: Communication protocol to use (tcp, unix etc.)
  564.      *  + hostspec: Host specification (hostname[:port])
  565.      *  + database: Database to use on the DBMS server
  566.      *  + username: User name for login
  567.      *  + password: Password for login
  568.      *
  569.      * @author Tomas V.V.Cox <cox@idecnet.com>
  570.      */
  571.     function parseDSN($dsn)
  572.     {
  573.         $parsed = array(
  574.             'phptype'  => false,
  575.             'dbsyntax' => false,
  576.             'username' => false,
  577.             'password' => false,
  578.             'protocol' => false,
  579.             'hostspec' => false,
  580.             'port'     => false,
  581.             'socket'   => false,
  582.             'database' => false,
  583.         );
  584.  
  585.         if (is_array($dsn)) {
  586.             $dsn = array_merge($parsed, $dsn);
  587.             if (!$dsn['dbsyntax']) {
  588.                 $dsn['dbsyntax'] = $dsn['phptype'];
  589.             }
  590.             return $dsn;
  591.         }
  592.  
  593.         // Find phptype and dbsyntax
  594.         if (($pos = strpos($dsn, '://')) !== false) {
  595.             $str = substr($dsn, 0, $pos);
  596.             $dsn = substr($dsn, $pos + 3);
  597.         } else {
  598.             $str = $dsn;
  599.             $dsn = null;
  600.         }
  601.  
  602.         // Get phptype and dbsyntax
  603.         // $str => phptype(dbsyntax)
  604.         if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {
  605.             $parsed['phptype']  = $arr[1];
  606.             $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2];
  607.         } else {
  608.             $parsed['phptype']  = $str;
  609.             $parsed['dbsyntax'] = $str;
  610.         }
  611.  
  612.         if (!count($dsn)) {
  613.             return $parsed;
  614.         }
  615.  
  616.         // Get (if found): username and password
  617.         // $dsn => username:password@protocol+hostspec/database
  618.         if (($at = strrpos($dsn,'@')) !== false) {
  619.             $str = substr($dsn, 0, $at);
  620.             $dsn = substr($dsn, $at + 1);
  621.             if (($pos = strpos($str, ':')) !== false) {
  622.                 $parsed['username'] = rawurldecode(substr($str, 0, $pos));
  623.                 $parsed['password'] = rawurldecode(substr($str, $pos + 1));
  624.             } else {
  625.                 $parsed['username'] = rawurldecode($str);
  626.             }
  627.         }
  628.  
  629.         // Find protocol and hostspec
  630.  
  631.         // $dsn => proto(proto_opts)/database
  632.         if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) {
  633.             $proto       = $match[1];
  634.             $proto_opts  = $match[2] ? $match[2] : false;
  635.             $dsn         = $match[3];
  636.  
  637.         // $dsn => protocol+hostspec/database (old format)
  638.         } else {
  639.             if (strpos($dsn, '+') !== false) {
  640.                 list($proto, $dsn) = explode('+', $dsn, 2);
  641.             }
  642.             if (strpos($dsn, '/') !== false) {
  643.                 list($proto_opts, $dsn) = explode('/', $dsn, 2);
  644.             } else {
  645.                 $proto_opts = $dsn;
  646.                 $dsn = null;
  647.             }
  648.         }
  649.  
  650.         // process the different protocol options
  651.         $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp';
  652.         $proto_opts = rawurldecode($proto_opts);
  653.         if ($parsed['protocol'] == 'tcp') {
  654.             if (strpos($proto_opts, ':') !== false) {
  655.                 list($parsed['hostspec'], $parsed['port']) = explode(':', $proto_opts);
  656.             } else {
  657.                 $parsed['hostspec'] = $proto_opts;
  658.             }
  659.         } elseif ($parsed['protocol'] == 'unix') {
  660.             $parsed['socket'] = $proto_opts;
  661.         }
  662.  
  663.         // Get dabase if any
  664.         // $dsn => database
  665.         if ($dsn) {
  666.             // /database
  667.             if (($pos = strpos($dsn, '?')) === false) {
  668.                 $parsed['database'] = rawurldecode($dsn);
  669.             // /database?param1=value1¶m2=value2
  670.             } else {
  671.                 $parsed['database'] = rawurldecode(substr($dsn, 0, $pos));
  672.                 $dsn = substr($dsn, $pos + 1);
  673.                 if (strpos($dsn, '&') !== false) {
  674.                     $opts = explode('&', $dsn);
  675.                 } else { // database?param1=value1
  676.                     $opts = array($dsn);
  677.                 }
  678.                 foreach ($opts as $opt) {
  679.                     list($key, $value) = explode('=', $opt);
  680.                     if (!isset($parsed[$key])) {
  681.                         // don't allow params overwrite
  682.                         $parsed[$key] = rawurldecode($value);
  683.                     }
  684.                 }
  685.             }
  686.         }
  687.  
  688.         return $parsed;
  689.     }
  690.  
  691.     // }}}
  692.     // {{{ assertExtension()
  693.  
  694.     /**
  695.      * Load a PHP database extension if it is not loaded already.
  696.      *
  697.      * @access public
  698.      *
  699.      * @param string $name the base name of the extension (without the .so or
  700.      *                     .dll suffix)
  701.      *
  702.      * @return boolean true if the extension was already or successfully
  703.      *                 loaded, false if it could not be loaded
  704.      */
  705.     function assertExtension($name)
  706.     {
  707.         if (!extension_loaded($name)) {
  708.             $dlext = OS_WINDOWS ? '.dll' : '.so';
  709.             $dlprefix = OS_WINDOWS ? 'php_' : '';
  710.             @dl($dlprefix . $name . $dlext);
  711.             return extension_loaded($name);
  712.         }
  713.         return true;
  714.     }
  715.     // }}}
  716. }
  717.  
  718. // }}}
  719. // {{{ class DB_Error
  720.  
  721. /**
  722.  * DB_Error implements a class for reporting portable database error
  723.  * messages.
  724.  *
  725.  * @package  DB
  726.  * @author Stig Bakken <ssb@php.net>
  727.  */
  728. class DB_Error extends PEAR_Error
  729. {
  730.     // {{{ constructor
  731.  
  732.     /**
  733.      * DB_Error constructor.
  734.      *
  735.      * @param mixed   $code   DB error code, or string with error message.
  736.      * @param integer $mode   what "error mode" to operate in
  737.      * @param integer $level  what error level to use for $mode & PEAR_ERROR_TRIGGER
  738.      * @param mixed   $debuginfo  additional debug info, such as the last query
  739.      *
  740.      * @access public
  741.      *
  742.      * @see PEAR_Error
  743.      */
  744.     function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
  745.               $level = E_USER_NOTICE, $debuginfo = null)
  746.     {
  747.         if (is_int($code)) {
  748.             $this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code, $mode, $level, $debuginfo);
  749.         } else {
  750.             $this->PEAR_Error("DB Error: $code", DB_ERROR, $mode, $level, $debuginfo);
  751.         }
  752.     }
  753.     // }}}
  754. }
  755.  
  756. // }}}
  757. // {{{ class DB_result
  758.  
  759. /**
  760.  * This class implements a wrapper for a DB result set.
  761.  * A new instance of this class will be returned by the DB implementation
  762.  * after processing a query that returns data.
  763.  *
  764.  * @package  DB
  765.  * @author Stig Bakken <ssb@php.net>
  766.  */
  767. class DB_result
  768. {
  769.     // {{{ properties
  770.  
  771.     var $dbh;
  772.     var $result;
  773.     var $row_counter = null;
  774.  
  775.     /**
  776.      * for limit queries, the row to start fetching
  777.      * @var integer
  778.      */
  779.     var $limit_from  = null;
  780.  
  781.     /**
  782.      * for limit queries, the number of rows to fetch
  783.      * @var integer
  784.      */
  785.     var $limit_count = null;
  786.  
  787.     // }}}
  788.     // {{{ constructor
  789.  
  790.     /**
  791.      * DB_result constructor.
  792.      * @param resource &$dbh   DB object reference
  793.      * @param resource $result  result resource id
  794.      * @param array    $options assoc array with optional result options
  795.      */
  796.     function DB_result(&$dbh, $result, $options = array())
  797.     {
  798.         $this->dbh = &$dbh;
  799.         $this->result = $result;
  800.         foreach ($options as $key => $value) {
  801.             $this->setOption($key, $value);
  802.         }
  803.         $this->limit_type  = $dbh->features['limit'];
  804.         $this->autofree    = $dbh->options['autofree'];
  805.         $this->fetchmode   = $dbh->fetchmode;
  806.         $this->fetchmode_object_class = $dbh->fetchmode_object_class;
  807.     }
  808.  
  809.     function setOption($key, $value = null)
  810.     {
  811.         switch ($key) {
  812.             case 'limit_from':
  813.                 $this->limit_from = $value; break;
  814.             case 'limit_count':
  815.                 $this->limit_count = $value; break;
  816.         }
  817.     }
  818.  
  819.     // }}}
  820.     // {{{ fetchRow()
  821.  
  822.     /**
  823.      * Fetch a row of data and return it by reference into an array.
  824.      *
  825.      * The type of array returned can be controlled either by setting this
  826.      * method's <var>$fetchmode</var> parameter or by changing the default
  827.      * fetch mode setFetchMode() before calling this method.
  828.      *
  829.      * There are two options for standardizing the information returned
  830.      * from databases, ensuring their values are consistent when changing
  831.      * DBMS's.  These portability options can be turned on when creating a
  832.      * new DB object or by using setOption().
  833.      *
  834.      *   + <samp>DB_PORTABILITY_LOWERCASE</samp>
  835.      *     convert names of fields to lower case
  836.      *
  837.      *   + <samp>DB_PORTABILITY_RTRIM</samp>
  838.      *     right trim the data
  839.      *
  840.      * @param int $fetchmode  how the resulting array should be indexed
  841.      * @param int $rownum     the row number to fetch
  842.      *
  843.      * @return array  a row of data, null on no more rows or PEAR_Error
  844.      *                object on error
  845.      *
  846.      * @see DB_common::setOption(), DB_common::setFetchMode()
  847.      * @access public
  848.      */
  849.     function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null)
  850.     {
  851.         if ($fetchmode === DB_FETCHMODE_DEFAULT) {
  852.             $fetchmode = $this->fetchmode;
  853.         }
  854.         if ($fetchmode === DB_FETCHMODE_OBJECT) {
  855.             $fetchmode = DB_FETCHMODE_ASSOC;
  856.             $object_class = $this->fetchmode_object_class;
  857.         }
  858.         if ($this->limit_from !== null) {
  859.             if ($this->row_counter === null) {
  860.                 $this->row_counter = $this->limit_from;
  861.                 // Skip rows
  862.                 if ($this->limit_type == false) {
  863.                     $i = 0;
  864.                     while ($i++ < $this->limit_from) {
  865.                         $this->dbh->fetchInto($this->result, $arr, $fetchmode);
  866.                     }
  867.                 }
  868.             }
  869.             if ($this->row_counter >= (
  870.                     $this->limit_from + $this->limit_count))
  871.             {
  872.                 if ($this->autofree) {
  873.                     $this->free();
  874.                 }
  875.                 $tmp = null;
  876.                 return $tmp;
  877.             }
  878.             if ($this->limit_type == 'emulate') {
  879.                 $rownum = $this->row_counter;
  880.             }
  881.             $this->row_counter++;
  882.         }
  883.         $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum);
  884.         if ($res === DB_OK) {
  885.             if (isset($object_class)) {
  886.                 // default mode specified in DB_common::fetchmode_object_class property
  887.                 if ($object_class == 'stdClass') {
  888.                     $arr = (object) $arr;
  889.                 } else {
  890.                     $arr = &new $object_class($arr);
  891.                 }
  892.             }
  893.             return $arr;
  894.         }
  895.         if ($res == null && $this->autofree) {
  896.             $this->free();
  897.         }
  898.         return $res;
  899.     }
  900.  
  901.     // }}}
  902.     // {{{ fetchInto()
  903.  
  904.     /**
  905.      * Fetch a row of data into an array which is passed by reference.
  906.      *
  907.      * The type of array returned can be controlled either by setting this
  908.      * method's <var>$fetchmode</var> parameter or by changing the default
  909.      * fetch mode setFetchMode() before calling this method.
  910.      *
  911.      * There are two options for standardizing the information returned
  912.      * from databases, ensuring their values are consistent when changing
  913.      * DBMS's.  These portability options can be turned on when creating a
  914.      * new DB object or by using setOption().
  915.      *
  916.      *   + <samp>DB_PORTABILITY_LOWERCASE</samp>
  917.      *     convert names of fields to lower case
  918.      *
  919.      *   + <samp>DB_PORTABILITY_RTRIM</samp>
  920.      *     right trim the data
  921.      *
  922.      * @param array &$arr       (reference) array where data from the row
  923.      *                          should be placed
  924.      * @param int   $fetchmode  how the resulting array should be indexed
  925.      * @param int   $rownum     the row number to fetch
  926.      *
  927.      * @return mixed  DB_OK on success, null on no more rows or
  928.      *                a DB_Error object on error
  929.      *
  930.      * @see DB_common::setOption(), DB_common::setFetchMode()
  931.      * @access public
  932.      */
  933.     function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null)
  934.     {
  935.         if ($fetchmode === DB_FETCHMODE_DEFAULT) {
  936.             $fetchmode = $this->fetchmode;
  937.         }
  938.         if ($fetchmode === DB_FETCHMODE_OBJECT) {
  939.             $fetchmode = DB_FETCHMODE_ASSOC;
  940.             $object_class = $this->fetchmode_object_class;
  941.         }
  942.         if ($this->limit_from !== null) {
  943.             if ($this->row_counter === null) {
  944.                 $this->row_counter = $this->limit_from;
  945.                 // Skip rows
  946.                 if ($this->limit_type == false) {
  947.                     $i = 0;
  948.                     while ($i++ < $this->limit_from) {
  949.                         $this->dbh->fetchInto($this->result, $arr, $fetchmode);
  950.                     }
  951.                 }
  952.             }
  953.             if ($this->row_counter >= (
  954.                     $this->limit_from + $this->limit_count))
  955.             {
  956.                 if ($this->autofree) {
  957.                     $this->free();
  958.                 }
  959.                 return null;
  960.             }
  961.             if ($this->limit_type == 'emulate') {
  962.                 $rownum = $this->row_counter;
  963.             }
  964.  
  965.             $this->row_counter++;
  966.         }
  967.         $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum);
  968.         if ($res === DB_OK) {
  969.             if (isset($object_class)) {
  970.                 // default mode specified in DB_common::fetchmode_object_class property
  971.                 if ($object_class == 'stdClass') {
  972.                     $arr = (object) $arr;
  973.                 } else {
  974.                     $arr = new $object_class($arr);
  975.                 }
  976.             }
  977.             return DB_OK;
  978.         }
  979.         if ($res == null && $this->autofree) {
  980.             $this->free();
  981.         }
  982.         return $res;
  983.     }
  984.  
  985.     // }}}
  986.     // {{{ numCols()
  987.  
  988.     /**
  989.      * Get the the number of columns in a result set.
  990.      *
  991.      * @return int the number of columns, or a DB error
  992.      *
  993.      * @access public
  994.      */
  995.     function numCols()
  996.     {
  997.         return $this->dbh->numCols($this->result);
  998.     }
  999.  
  1000.     // }}}
  1001.     // {{{ numRows()
  1002.  
  1003.     /**
  1004.      * Get the number of rows in a result set.
  1005.      *
  1006.      * @return int the number of rows, or a DB error
  1007.      *
  1008.      * @access public
  1009.      */
  1010.     function numRows()
  1011.     {
  1012.         return $this->dbh->numRows($this->result);
  1013.     }
  1014.  
  1015.     // }}}
  1016.     // {{{ nextResult()
  1017.  
  1018.     /**
  1019.      * Get the next result if a batch of queries was executed.
  1020.      *
  1021.      * @return bool true if a new result is available or false if not.
  1022.      *
  1023.      * @access public
  1024.      */
  1025.     function nextResult()
  1026.     {
  1027.         return $this->dbh->nextResult($this->result);
  1028.     }
  1029.  
  1030.     // }}}
  1031.     // {{{ free()
  1032.  
  1033.     /**
  1034.      * Frees the resources allocated for this result set.
  1035.      * @return  int error code
  1036.      *
  1037.      * @access public
  1038.      */
  1039.     function free()
  1040.     {
  1041.         $err = $this->dbh->freeResult($this->result);
  1042.         if (DB::isError($err)) {
  1043.             return $err;
  1044.         }
  1045.         $this->result = false;
  1046.         return true;
  1047.     }
  1048.  
  1049.     // }}}
  1050.     // {{{ tableInfo()
  1051.  
  1052.     /**
  1053.      * @deprecated
  1054.      * @internal
  1055.      * @see DB_common::tableInfo()
  1056.      */
  1057.     function tableInfo($mode = null)
  1058.     {
  1059.         if (is_string($mode)) {
  1060.             return $this->dbh->raiseError(DB_ERROR_NEED_MORE_DATA);
  1061.         }
  1062.         return $this->dbh->tableInfo($this, $mode);
  1063.     }
  1064.  
  1065.     // }}}
  1066.     // {{{ getRowCounter()
  1067.  
  1068.     /**
  1069.      * returns the actual row number
  1070.      * @return integer
  1071.      */
  1072.     function getRowCounter()
  1073.     {
  1074.         return $this->row_counter;
  1075.     }
  1076.     // }}}
  1077. }
  1078.  
  1079. // }}}
  1080. // {{{ class DB_row
  1081.  
  1082. /**
  1083.  * Pear DB Row Object
  1084.  * @see DB_common::setFetchMode()
  1085.  */
  1086. class DB_row
  1087. {
  1088.     // {{{ constructor
  1089.  
  1090.     /**
  1091.      * constructor
  1092.      *
  1093.      * @param resource row data as array
  1094.      */
  1095.     function DB_row(&$arr)
  1096.     {
  1097.         foreach ($arr as $key => $value) {
  1098.             $this->$key = &$arr[$key];
  1099.         }
  1100.     }
  1101.  
  1102.     // }}}
  1103. }
  1104.  
  1105. // }}}
  1106.  
  1107. /*
  1108.  * Local variables:
  1109.  * tab-width: 4
  1110.  * c-basic-offset: 4
  1111.  * End:
  1112.  */
  1113.  
  1114. ?>
  1115.